package gs

import "errors"

type LStr struct {
	Str
	Start int
	End   int
}

func (str Str) TextLoc(start string, end string) (lstrs List[LStr]) {
	offset := 0
	str.Extract(start, end).Every(func(no int, i Str) {
		if s, e := str[offset:].Index(i); s > -1 {
			lstrs = lstrs.Add(LStr{
				Str:   i,
				Start: s + offset,
				End:   e + offset,
			})
			offset += e
		}
	})
	return
}

func (str Str) TextLocRe(start string, end string) (lstrs List[LStr]) {
	offset := 0
	str.ExtractByRE(start, end).Every(func(no int, i Str) {
		if s, e := str[offset:].Index(i); s > -1 {
			lstrs = lstrs.Add(LStr{
				Str:   i,
				Start: s + offset,
				End:   e + offset,
			})
			offset += e
		}
	})
	return
}

func (str Str) TextChangeLine(lineNo int, changeFunc func(textSlice Str) Str) Str {
	nstrs := List[Str]{}
	str.EveryLine(func(lineno int, line Str) {
		if lineno == lineNo {
			nstrs = nstrs.Add(changeFunc(line))
		} else {
			nstrs = nstrs.Add(line)
		}
	})
	return nstrs.Join("\n")
}

func (str Str) TextChangeAfter(locStr LStr, changeFunc func(oldAfter Str) (newAfter Str)) Str {
	newAfterStr := changeFunc(str[locStr.End:])
	return str[:locStr.End] + newAfterStr
}

func (str Str) TextChangeBefore(locStr LStr, changeFunc func(oldBefore Str) (newBefore Str)) Str {
	newBeforeStr := changeFunc(str[:locStr.Start])
	return newBeforeStr + str[locStr.Start:]
}

func (str Str) TextChangeIn(locStr LStr, changeFunc func(oldIn Str) (newIn Str)) Str {
	newInStr := changeFunc(str[locStr.Start:locStr.End])
	return str[:locStr.Start] + newInStr + str[locStr.End:]
}

func (str Str) TextChange(pre, end string, changeFunc func(oldIn Str) (newIn Str)) Str {
	e := str.TextLoc(pre, end)
	if e.Count() == 0 {
		str.Println(pre, "|", end)
		panic(errors.New("no loc found!"))
	}
	locStr := e.Nth(0)

	newInStr := changeFunc(str[locStr.Start+len(pre) : locStr.End-len(end)])
	return str[:locStr.Start+len(pre)] + newInStr + str[locStr.End-len(end):]
}

func (str Str) TextChangeRe(pre, end string, changeFunc func(oldIn Str) (newIn Str)) Str {
	locStr := str.TextLocRe(pre, end).Nth(0)
	newInStr := changeFunc(str[locStr.Start+len(pre) : locStr.End-len(end)])
	return str[:locStr.Start+len(pre)] + newInStr + str[locStr.End-len(end):]
}

func (str Str) TextLineContains(key Str, changeFunc func(oldIn Str) Str) Str {
	nstrs := List[Str]{}
	str.EveryLine(func(lineno int, line Str) {
		if line.In(key) {
			nstrs = nstrs.Add(changeFunc(line))
		} else {
			nstrs = nstrs.Add(line)
		}
	})
	return nstrs.Join("\n")
}

func (str Str) TextEveryLineWith(linechangeFunc func(lineno int, oldLine Str) Str) Str {
	nstrs := List[Str]{}
	// fmt.Println("hit b")
	str.EveryLine(func(lineno int, line Str) {
		// line.Println(lineno)

		nstrs = nstrs.Add(linechangeFunc(lineno, line))
	})
	return nstrs.Join("\n")
}
